package com.yanboo.modules.mnt.service.impl;

import com.querydsl.jpa.impl.JPAQueryFactory;
import com.yanboo.constant.ProcessorMsgType;
import com.yanboo.modules.mnt.websocket.processor.StatusWSService;
import com.yanboo.modules.mnt.websocket.processor.dto.ProcessorStatusDto;
import com.yanboo.modules.mnt.websocket.processor.dto.ProcessorWSMsgDto;
import com.yanboo.processor.CustomDefaultExecutor;
import com.yanboo.constant.ProcessorStatus;
import com.yanboo.modules.mnt.domain.Processor;
import com.yanboo.common.utils.ValidationUtil;
import com.yanboo.common.utils.FileUtil;
import lombok.RequiredArgsConstructor;
import com.yanboo.modules.mnt.repository.ProcessorRepository;
import com.yanboo.modules.mnt.service.ProcessorService;
import com.yanboo.modules.mnt.service.dto.ProcessorDto;
import com.yanboo.modules.mnt.service.dto.ProcessorQueryCriteria;
import com.yanboo.modules.mnt.service.mapstruct.ProcessorMapper;

import org.apache.logging.log4j.util.Strings;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.IdUtil;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import com.yanboo.common.utils.PageUtil;
import com.yanboo.common.utils.QueryHelp;

import java.io.File;
import java.util.*;
import java.io.IOException;

import jakarta.servlet.http.HttpServletResponse;

import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

import com.yanboo.common.utils.PageResult;

/**
 * @author fdq
 * @description 服务实现
 * @date 2024-03-27
 **/
@Service
@RequiredArgsConstructor
public class ProcessorServiceImpl implements ProcessorService {

    private final ProcessorRepository processorRepository;
    private final ProcessorMapper processorMapper;

    private ConcurrentHashMap<Long, CustomDefaultExecutor> executorMap = new ConcurrentHashMap<>();

    @Override
    public PageResult<ProcessorDto> queryAll(ProcessorQueryCriteria criteria, Pageable pageable) {
        Page<Processor> page = processorRepository.findAll((root, criteriaQuery, criteriaBuilder)
                -> QueryHelp.getPredicate(root, criteria, criteriaBuilder), pageable);
        return PageUtil.toPage(page.map(processorMapper::toDto));
    }

    @Override
    public List<ProcessorDto> queryAll(ProcessorQueryCriteria criteria) {
        return processorMapper.toDto(processorRepository.findAll((root, criteriaQuery, criteriaBuilder)
                -> QueryHelp.getPredicate(root, criteria, criteriaBuilder)));
    }

    @Override
    @Transactional
    public ProcessorDto findById(Long id) {
        Processor processor = processorRepository.findById(id).orElseGet(Processor::new);
        ValidationUtil.isNull(processor.getId(), "Processor", "id", id);
        return processorMapper.toDto(processor);
    }

    @Override
    // @Transactional(rollbackFor = Exception.class)
    public void create(Processor resources) throws IOException, InterruptedException {
        if (Strings.isEmpty(resources.getCommand())) {
            throw new RuntimeException("命令不能为空");
        }
        Snowflake snowflake = IdUtil.createSnowflake(1, 1);
        resources.setId(snowflake.nextId());
        Processor save = processorRepository.saveAndFlush(resources);
        if (resources.getTargetStatus().equals(ProcessorStatus.RUNNING)) {
            execAsync(processorMapper.toDto(save));
        }
    }

    @Override
    //@Transactional(rollbackFor = Exception.class)
    public void update(ProcessorDto resources) {
        Processor processor = processorRepository.findById(resources.getId()).orElseGet(Processor::new);
        ValidationUtil.isNull(processor.getId(), "Processor", "id", resources.getId());
        resources.setStatus(processor.getStatus()).setTargetStatus(processor.getTargetStatus());
        processor.copy(processorMapper.toEntity( resources));
        processorRepository.saveAndFlush(processor);
    }

    @Override
    public void deleteAll(Long[] ids) {
        for (Long id : ids) {
            processorRepository.deleteById(id);
        }
    }

    @Override
    public void download(List<ProcessorDto> all, HttpServletResponse response) throws IOException {
        List<Map<String, Object>> list = new ArrayList<>();
        for (ProcessorDto processor : all) {
            Map<String, Object> map = new LinkedHashMap<>();
            map.put("命令", processor.getCommand());
            map.put("进程id", processor.getPid());
            map.put("状态", processor.getStatus());
            map.put("名称", processor.getName());
            map.put("目标状态", processor.getTargetStatus());
            map.put("createBy", processor.getCreateBy());
            map.put("createTime", processor.getCreateTime());
            map.put("updateBy", processor.getUpdateBy());
            map.put("updateTime", processor.getUpdateTime());
            map.put("描述", processor.getDescription());
            list.add(map);
        }
        FileUtil.downloadExcel(list, response);
    }


    @Override
    public void execAsync(ProcessorDto processor) throws IOException, InterruptedException {

        //没有输出重定向
//        if (!processor.getCommand().contains(">")){
//            processor.setCommand(processor.getCommand()+" > "+System.getProperty("user.dir")+ File.separator+"logs"+File.separator+processor.getName()+".log");
//        }
//        //processor.setCommand("java -jar E:/workspace/demo/target/demo-1.0-SNAPSHOT.jar");
//        //command = "ping 127.0.0.1 -t";
//        //接收正常结果流
//        CommandLine commandLine = CommandLine.parse(processor.getCommand());
//        CustomDefaultExecutor exec = CustomDefaultExecutor.builder().get();
//        processor.setTargetStatus(ProcessorStatus.RUNNING);
//        update(processor);
//        StdLogOutStream infoStream = new StdLogOutStream(Level.INFO, String.valueOf(processor.getId()));
//        StdLogOutStream errorStream = new StdLogOutStream(Level.SEVERE, String.valueOf(processor.getId()));
//
//        PumpStreamHandler streamHandler = new PumpStreamHandler(infoStream, errorStream);
//        exec.setStreamHandler(streamHandler);
//        ExecuteResultHandler erh = new ExecuteResultHandler() {
//            @Override
//            public void onProcessComplete(int exitValue) {
//                processor.setStatus(ProcessorStatus.FINISH);
//                processorRepository.saveAndFlush(processor);
//                LogWSService.sendMsg(processor.getId(), new ProcessorWSMsgDto().setType(ProcessorMsgType.LOG)
//                        .setMsg(new ProcessorLogDto().setLog(processor.getName() + "进程执行完成").setLevel(Level.INFO)));
//                StatusWSService.sendMsg(processor.getId(), new ProcessorWSMsgDto().setType(ProcessorMsgType.STATUS)
//                        .setMsg(new ProcessorStatusDto().setId(String.valueOf(processor.getId())).setStatus(ProcessorStatus.FINISH).setPid(null)));
//            }
//
//            @Override
//            public void onProcessFailed(ExecuteException e) {
//                processor.setStatus(ProcessorStatus.ERROR);
//                processorRepository.saveAndFlush(processor);
//                LogWSService.sendMsg(processor.getId(), new ProcessorWSMsgDto().setType(ProcessorMsgType.LOG)
//                        .setMsg(new ProcessorLogDto().setLog(processor.getName() + "进程执行失败").setLevel(Level.SEVERE)));
//                StatusWSService.sendMsg(processor.getId(), new ProcessorWSMsgDto().setType(ProcessorMsgType.STATUS)
//                        .setMsg(new ProcessorStatusDto().setId(String.valueOf(processor.getId())).setStatus(ProcessorStatus.ERROR).setPid(null)));
//            }
//        };
//        LogWSService.sendMsg(processor.getId(), new ProcessorWSMsgDto().setType(ProcessorMsgType.LOG)
//                .setMsg(new ProcessorLogDto().setLog(processor.getName() + "开始执行").setLevel(Level.INFO)));
//        String finalCommand = processor.getCommand();
//        exec.execute(commandLine, erh, process -> {
//            System.out.println("异步执行中");
//            long pid = process.pid();
//            System.out.println("进程id:" + pid);
//            System.out.println(finalCommand);
//            processor.setStatus(ProcessorStatus.RUNNING);
//            processor.setPid(pid);
//            processorRepository.saveAndFlush(processor);
//            StatusWSService.sendMsg(processor.getId(), new ProcessorWSMsgDto().setType(ProcessorMsgType.STATUS)
//                    .setMsg(new ProcessorStatusDto().setId(String.valueOf(processor.getId())).setStatus(ProcessorStatus.RUNNING).setPid(String.valueOf(pid))));
//        });

        List<String> commands = new ArrayList<>();
//        if (OS.isFamilyWindows() || OS.isFamilyWin9x() || OS.isFamilyWinNT()) {
//            commands.add("cmd");
//            commands.add("/c");
//        }else {
//            commands.add("sh");
//        }
        String filePath = "./logs/"+processor.getName()+".log";
        String command = processor.getCommand();
        if (processor.getCommand().contains(">")) {
            String[] args = command.split(">");
            command = args[0].trim();
            filePath = args[1].trim();
        }
        for (String s : command.split(" ")) {
            commands.add(s.trim());
        }
        commands.add(command);
        ProcessBuilder builder = new ProcessBuilder(commands);
        File out = new File(filePath);
        builder.redirectOutput(out);
        builder.redirectError(out);
        builder.redirectInput(out);
        builder.environment().putAll(System.getenv());
        Process process = builder.start();
        long pid = process.pid();
        processor.setStatus(process.isAlive()?ProcessorStatus.RUNNING:ProcessorStatus.ERROR);
        processor.setPid(pid);
        processorRepository.saveAndFlush(processorMapper.toEntity(processor));
        StatusWSService.sendMsg(processor.getId(), new ProcessorWSMsgDto().setType(ProcessorMsgType.STATUS)
                    .setMsg(new ProcessorStatusDto()
                            .setId(String.valueOf(processor.getId()))
                            .setStatus(process.isAlive()?ProcessorStatus.RUNNING:ProcessorStatus.ERROR)
                            .setPid(String.valueOf(pid))));
    }

    public void destroy(ProcessorDto processor) {
        Optional<ProcessHandle> processHandle = ProcessHandle.of(processor.getPid());
        processHandle.ifPresent(ProcessHandle::destroy);
        processor.setStatus(ProcessorStatus.EXIT).setTargetStatus(ProcessorStatus.EXIT);
        processor.setPid(null);
        processorRepository.saveAndFlush(processorMapper.toEntity( processor));
        StatusWSService.sendMsg(processor.getId(), new ProcessorWSMsgDto().setType(ProcessorMsgType.STATUS)
                .setMsg(new ProcessorStatusDto()
                        .setId(String.valueOf(processor.getId()))
                        .setStatus(processor.getStatus())
                        .setPid("")));
    }

    public void destroyForcibly(ProcessorDto processor) {
        Optional<ProcessHandle> processHandle = ProcessHandle.of(processor.getPid());
        processHandle.ifPresent(ProcessHandle::destroyForcibly);
        processor.setStatus(ProcessorStatus.EXIT).setTargetStatus(ProcessorStatus.EXIT);
        update(processor);
    }

    @Override
    public void reAttach() {
        List<ProcessorDto> processorDtos = queryAll(new ProcessorQueryCriteria().setTargetStatus(ProcessorStatus.RUNNING.name()));
        for (ProcessorDto processorDto : processorDtos) {
            //TODO 根据PID更新进程状态
        }
    }
}